﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Security.Principal;
using Monitor.Models.ForWorkWithRepository;
using Monitor.Models.Interfaces;
using Monitor.Models.Json;
using Monitor.Models.JsonModel;
using Monitor.Repository.DataRepository.LinqModel;
using ServiceStack.Text;


namespace Monitor.Repository.DataRepository
{

    #region DataRepository - описание доступных разделов

    public class DataRepository:Models.Interfaces.IDataRepository
    {

        #region переменные
        //хранилище таблиц
        private OracleBaseEntities oracleBase;
        private Models.Interfaces.ILogger _logger;
        private BakRepositoryClass bakRepository;
        private CarRepositoryClass carRepository;
        private MesuringRepositoryClass mesuringRepository;
        private ServiceRepositoryClass serviceRepository;
        private ClassificatorClass classificatorRepository;

        //хранилище информации о БАК-ах
        public Models.Interfaces.IBakRepository BakRepository { get { return bakRepository; } }
        //хранилище информации о машинах
        public Models.Interfaces.ICarRepository CarRepository { get { return carRepository; } }
        //хранилище информации об измерениях
        public Models.Interfaces.IMesuringRepository MesuringRepository { get { return mesuringRepository; } }
        //хранилище информации о сервисных службах
        public Models.Interfaces.IServiceRepository ServiceRepository { get { return serviceRepository; } }
        //хранилище классификатора
        public Models.Interfaces.IClassificatorRepository ClassificatorRepository { get { return classificatorRepository; } }
        #endregion

        #region конструктор

        public DataRepository(Models.Interfaces.ILogger logger)
        {
            _logger = logger;
            oracleBase = new OracleBaseEntities();
            bakRepository = new BakRepositoryClass(oracleBase);
            carRepository = new CarRepositoryClass(oracleBase);
            mesuringRepository = new MesuringRepositoryClass(oracleBase);
            serviceRepository = new ServiceRepositoryClass(oracleBase);
            classificatorRepository = new ClassificatorClass(oracleBase);
        }

        #endregion

        #region освобождение ресурсов
        
        public void Dispose()
        {
            oracleBase.Dispose();
        }

        #endregion
    }

    #endregion

    #region BakRepositoryClass - хранилище информации о БАК-ах

    public class BakRepositoryClass : Models.Interfaces.IBakRepository
    {
        #region инициализация

        private OracleBaseEntities _oracleBase;

        public BakRepositoryClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
        }

        #endregion

        #region Get

        public IEnumerable<Bak> GetBak()
        {
            return _oracleBase.BakTable;
        }

        public Bak GetBak(Int64 id)
        {
            return _oracleBase.BakTable.Find(id);
        }

        public IEnumerable<Bak> FreeBak
        {get { return _oracleBase.BakTable.Where(n=>!_oracleBase.CarTable.Any(m=>m.BakId==n.BakId)); }
        }

        #endregion

        #region Add

        public Bak AddBak(Models.Json.BakInput model)
        {
            if (model.IsValid)
            {
                if (_oracleBase.BakTable.Any(n => n.SerialNumber == model.SerialNumber.Value))
                {
                    throw new IdentityNotMappedException("Указанный серийный номер уже принадлежит другому устройству");
                }
                Bak vdv = new Bak()
                              {
                                  Date = model.Date.Value,
                                  OperationOrganization = model.Expluat.Value,
                                  Produser = model.Creator,
                                  SerialNumber = model.SerialNumber.Value,
                                  Type = model.Type.Value
                              };
                _oracleBase.BakTable.Add(vdv);
                _oracleBase.SaveChanges();
                return vdv;
            }
            else
            {
                throw new Exception("BakRepositoryClass - AddBak Модель некорректна: " + model.ToJson());
            }

        }

        #endregion

        #region Редактирование

        public void EditBak(Models.Json.BakInput model)
        {
            Bak current = _oracleBase.BakTable.Find(model.Id.Value);
            if (current != null)
            {
                current.SerialNumber = model.SerialNumber.Value;
                current.Produser = model.Creator;
                current.Type = model.Type.Value;
                current.Date = model.Date.Value;
                current.OperationOrganization = model.Expluat.Value;
                _oracleBase.SaveChanges();
            }
            else
            {
                throw new IdentityNotMappedException("Элемент не найден в базе данных");
            }
        }

        #endregion

        #region Удаление

        public void DeleteBak(Int64 id)
        {
            Bak current = _oracleBase.BakTable.Find(id);
            if (current != null)
            {
                _oracleBase.BakTable.Remove(current);
                _oracleBase.SaveChanges();
            }
        }

        #endregion

    }

    #endregion

    #region CarRepositoryClass - хранилище информации о машинах

    public class CarRepositoryClass : Models.Interfaces.ICarRepository
    {
        private OracleBaseEntities _oracleBase;

        public CarRepositoryClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
        }

        public IEnumerable<Car> GetCars()
        {
            return _oracleBase.CarTable;
        }
        public Car GetCarById(Int64 id)
        {
            return _oracleBase.CarTable.Find(id);
        }


        public Car AddCar(CarInput model)
        {
            if(model.IsValid)
            {
                //Bak currentBak = _oracleBase.BakTable.Find(model.BackId.Value);
                if(!_oracleBase.CarTable.Any(n=>n.BakId==model.BackId.Value))
                {
                    Car newItem = new Car()
                                      {
                                          BakId = model.BackId.Value,
                                          Type = model.Type.Value,
                                          Date = model.Date.Value,
                                          SerialNumber = model.SerialNumber,
                                          OperationOrganization = model.OperationOrganization.Value
                                      };
                    _oracleBase.CarTable.Add(newItem);
                    _oracleBase.SaveChanges();

                    Int64  carId = _oracleBase.CarTable.Max(n => n.CarId);
                    foreach (var cm in model.Mesuring)
                    {
                        Param curM = new Param()

                        {
                            CarId = carId,
                            ParamId = cm.MesuringId,
                            Date = DateTime.Now,
                            Period = cm.TimeOut
                        };
                        _oracleBase.ParamTable.Add(curM);
                    }

                    _oracleBase.SaveChanges();
                    

                    
                    
                }
                else
                {
                    throw new EvaluateException("Выбранный БАК установлен на другой машине");
                }
                
            }
            return null;
        }

        public void EditCar(CarInput model)
        {
            if (model.IsValid)
            {
                Car curItem = GetCarById(model.Id.Value);
                if (curItem != null)
                {
                    if (model.BackId.Value == curItem.BakId || !_oracleBase.CarTable.Any(n => n.BakId == model.BackId.Value))
                    {
                        curItem.BakId = model.BackId.Value;
                        curItem.Type = model.Type.Value;
                        curItem.Date = model.Date.Value;
                        curItem.SerialNumber = model.SerialNumber;
                        curItem.OperationOrganization = model.OperationOrganization.Value;

                        foreach (Param curParam in _oracleBase.ParamTable.Where(n => n.CarId == curItem.CarId))
                        {
                            _oracleBase.ParamTable.Remove(curParam);
                        }
                        _oracleBase.SaveChanges();
                        foreach (var cm in model.Mesuring)
                        {
                            Param curM = new Param()
                            {
                                CarId = curItem.CarId,
                                ParamId = cm.MesuringId,
                                Date = DateTime.Now,
                                Period = cm.TimeOut
                            };
                            _oracleBase.ParamTable.Add(curM);
                        }

                        _oracleBase.SaveChanges();
                    }
                    else
                    {
                        throw new EvaluateException("Выбранный БАК установлен на другой машине");
                    }
                }
                else
                {
                    throw new EvaluateException("В базе отсутствует указанная машина");
                }
            }
        }
        public void DeleteCar(Int64 id)
        {
            Car curItem = GetCarById(id);
            if (curItem!=null)
            {
                _oracleBase.CarTable.Remove(curItem);
                _oracleBase.SaveChanges();
            }
        }
    }

    #endregion

    #region MesuringRepositoryClass - хранилище информации об измерениях

    public class MesuringRepositoryClass : Models.Interfaces.IMesuringRepository
    {
        private OracleBaseEntities _oracleBase;
        public MesuringRepositoryClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
            CoordRepository = new CoordRepositoryClass(_oracleBase);
            DataValueRepository=new DataValueRepositoryClass(_oracleBase);

        }
        public ICoordRepository CoordRepository { get; private set; }
        public IDataValueRepository DataValueRepository { get; private set; }

    }

    #endregion

    #region DataValueRepositoryClass - хранилище информации о местоположении

    public class DataValueRepositoryClass : IDataValueRepository
    {
        long[] CAN_Id
        {
            get
            {
                return _oracleBase
                    .Classificator
                    .Where(n => n.MasterId == 20010000000)
                    .Select(n => n.Id)
                    .ToArray();
            }
        }

        #region конструктор
        private OracleBaseEntities _oracleBase;
        public DataValueRepositoryClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
        }
        #endregion

        #region DataValue GetDataValue(Int64 p_Id, Int64 m_Id, DateTime date)

        public DataValue GetDataValue(Int64 p_Id, Int64 m_Id, DateTime date)
        {
            DataValue result = new DataValue()
                                   {
                                       CarId = p_Id,
                                       MesuringId = m_Id,
                                       Date = date,Value = null
                                   };
            RegDataRow curPosition = _oracleBase.RegTable.FirstOrDefault(n => n.CarId == p_Id && n.RegDate == date);
            if(curPosition!=null)
            {
                if(CAN_Id.Contains(m_Id))
                {
                    RowCAN mesuring = curPosition.DataFromCAN.FirstOrDefault(n => n.RegId == m_Id);
                    if(mesuring!=null)
                    {
                        result.Value = mesuring.Value;
                    }
                }
            }
            return result;
        }

        #endregion

        #region DataValue GetLastDataValue(Int64 p_Id, Int64 m_Id)

        public DataValue GetLastDataValue(Int64 p_Id, Int64 m_Id)
        {
            DataValue result = new DataValue()
            {
                CarId = p_Id,
                MesuringId = m_Id,
                Value = null
            };
            if (CAN_Id.Contains(m_Id))
            {
                RegDataRow lastPosition = _oracleBase
                    .RegTable.Where(n => n.CarId == p_Id)
                    .OrderByDescending(n => n.RegDate)
                    .FirstOrDefault(n => n.DataFromCAN.Any(m=>m.ParamId==m_Id));
                if(lastPosition!=null)
                {
                    RowCAN curValue = lastPosition.DataFromCAN.FirstOrDefault(n => n.ParamId == m_Id);
                    if(curValue!=null)
                    {
                        result.Value = curValue.Value;
                    }
                }
            }
            return result;

        }
        #endregion

    }

    #endregion

    #region CoordRepositoryClass - хранилище информации о местоположении

    public class CoordRepositoryClass : ICoordRepository
    {
        #region конструктор
        private OracleBaseEntities _oracleBase;
        public CoordRepositoryClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
        }
        #endregion

        #region RegDataRow GetLastPosition(Int64 id)
        public RegDataRow GetLastPosition(Int64 id)
        {
            return _oracleBase
                .RegTable
                .Where(n => n.CarId == id)
                .OrderByDescending(n => n.RegId)
                .FirstOrDefault();
        }

        #endregion

    }

    #endregion

    #region ServiceRepositoryClass - хранилище информации о сервисных службах

    public class ServiceRepositoryClass : Models.Interfaces.IServiceRepository
    {
        #region конструктор
        private OracleBaseEntities _oracleBase;
        public ServiceRepositoryClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
        }
        #endregion

        #region GET

        public Servise GetServise(Int64 id)
        {
            return _oracleBase.ServiseTable.Find(id);
        }

        #endregion

        #region AddOrUpdate

        public void AddOrUpdateService(Int64 id, Models.Json.ClassifDealerModel model)
        {
            Servise upd = _oracleBase.ServiseTable.Find(id);
            if (upd!=null)
            {
                upd.Contract = model.Contract;
            }
            else
            {
                upd = new Servise()
                          {
                              Id = id,
                              Contract = model.Contract
                          };
                _oracleBase.ServiseTable.Add(upd);
            }
            _oracleBase.SaveChanges();
        }

        #endregion
    }

    #endregion

    #region ClassificatorClass - классификатор

    public class ClassificatorClass : Models.Interfaces.IClassificatorRepository
    {
        #region инициализация
        private OracleBaseEntities _oracleBase;
        public ClassificatorClass(OracleBaseEntities oracleBase)
        {
            _oracleBase = oracleBase;
        }
        #endregion

        #region получить данные
        public Classif GetById(Int64 id)
        {
            return _oracleBase.Classificator.Find(id);
        }
        public IEnumerable<Classif> GetBranchTree(Int64? id)
        {

            if (id.HasValue)
            {
                return _oracleBase.Classificator.Where(n => n.MasterId == id.Value);
            }
            else
            {
                return _oracleBase.Classificator.Where(n => !n.MasterId.HasValue);
            }

        }
        #endregion

        #region добавить элемент
        public Classif Add(ClassifItem newItem)
        {
            Int64 id = 0;
            string treeS = null;
            #region определение Id
            
            if(newItem.MasterId.HasValue&&newItem.Id.HasValue)
            {
                //Int64 startIdL = Convert.ToInt64(Math.Truncate(newItem.MasterId.Value / 10000000000.0) * 10000000000);
                //Int64 endIdL = Convert.ToInt64((Math.Truncate(newItem.MasterId.Value/10000000000.0) + 1)*10000000000);
                //id = _oracleBase.Classificator.Where(n => n.Id < endIdL && n.Id >= startIdL)
                       // .Max(n => n.Id)+1;
                id = newItem.Id.Value;
                treeS = _oracleBase.Classificator.Find(newItem.MasterId.Value).Tree + "." +
                             newItem.MasterId.Value.ToString();
            }
            else
            {
                id =
                    Convert.ToInt64((Math.Truncate(_oracleBase.Classificator.Max(n => n.Id)/10000000000.0) + 1)*
                                    10000000000);
            }
            #endregion
            Classif current = new Classif()
                                  {
                                      Name = newItem.Name,
                                      FullName = newItem.FullName,
                                      Date = DateTime.Now,
                                      MasterId = newItem.MasterId,
                                      Id = id,
                                      Tree = treeS
                                  };
            _oracleBase.Classificator.Add(current);
            _oracleBase.SaveChanges();
            return current;
        }
        #endregion

        #region обновить элемент

        public void Update(ClassifItem upd)
        {
            if(upd.Id.HasValue)
            {
                Classif current = _oracleBase.Classificator.Find(upd.Id.Value);
                current.Name = upd.Name;
                current.FullName = upd.FullName;
                //current.MasterId = upd.MasterId;
                _oracleBase.SaveChanges();
            }
            else
            {
                throw new Exception("ClassificatorClass - Update - попытка обновить элемент без Id");
            }
        }

        #endregion

        #region удалить элемент
        public  void Delete(Int64 id)
        {
            Classif current = _oracleBase.Classificator.Find(id);
            string treeMap = string.Format("{0}.{1}", current.Tree, current.Id);
            foreach (Classif selectedRow in _oracleBase.Classificator.Where(n => n.Tree.Contains(treeMap)))
            {
                _oracleBase.Classificator.Remove(selectedRow);
            }

            //Servise curService = current.Servise;
            //if (curService != null)
            //{
            //    _oracleBase.ServiseTable.Remove(curService);
            //}

            _oracleBase.Classificator.Remove(current);
            _oracleBase.SaveChanges();
        }
        #endregion

    }

    #endregion

}